package com.example.myapplication.view

import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.*
import android.widget.LinearLayout
import android.widget.OverScroller
import android.widget.Scroller
import androidx.core.view.ViewCompat
import androidx.customview.widget.ViewDragHelper
import androidx.interpolator.view.animation.FastOutLinearInInterpolator
import com.example.myapplication.R
import kotlin.math.abs

/**
 * 可滑动的View
 */
class SideSlipLayout : LinearLayout {

    interface SideSlipListener {
        fun onMenuState(isShow: Boolean, contentView: View)
    }

    private val DURATION = 200
    private val mScroller: OverScroller
    private var mLeftContent: ViewGroup? = null
    private var mRightContent: ViewGroup? = null

    //速度计算器
    private var mVelocityTracker: VelocityTracker? = null

    //能计算出的fling速度
    private var mTouchSlop = 0
    private var mMinimumVelocity = 0
    private var mMaximumVelocity = 0
    private var curX = 0f
    private var mIsBeingDragged = false
    private var mActivePointerId: Int = ViewDragHelper.INVALID_POINTER
    private var mLastMotionX = 0

    var sildListener: SideSlipListener? = null

    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )

    init {
        mScroller = OverScroller(context, FastOutLinearInInterpolator())
        val configuration = ViewConfiguration.get(context)
        mTouchSlop = configuration.scaledTouchSlop
        mMinimumVelocity = configuration.scaledMinimumFlingVelocity
        mMaximumVelocity = configuration.scaledMaximumFlingVelocity
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val widthParentMeasureSize = MeasureSpec.getSize(widthMeasureSpec)
        if (mLeftContent == null || mRightContent == null) {
            mLeftContent = findViewById<ViewGroup>(R.id.tag_sild_slip_layout_left_view)
            mRightContent = findViewById<ViewGroup>(R.id.tag_sild_slip_layout_right_view)
        }
        if (mLeftContent == null || mRightContent == null) return
        measureChildWithMargins(
            mLeftContent,
            MeasureSpec.makeMeasureSpec(widthParentMeasureSize, MeasureSpec.EXACTLY),
            0,
            heightMeasureSpec,
            0
        )
        measureChildWithMargins(
            mRightContent,
            widthMeasureSpec,
            0,
            MeasureSpec.makeMeasureSpec(mLeftContent!!.measuredHeight, MeasureSpec.EXACTLY),
            0
        )
        setMeasuredDimension(mLeftContent!!.measuredWidth, mLeftContent!!.measuredHeight)
    }

    fun setOpen(open: Boolean) {
        if (open) {
            scrollTo(mRightContent!!.measuredWidth, 0)
        } else {
            scrollTo(0, 0)
        }
    }

    fun setOpenByAnimation(open: Boolean) {
        if (open) {
            if (!mScroller.isFinished) mScroller.forceFinished(true)
            val scrollX = scrollX
            mScroller.startScroll(
                abs(scrollX), 0,
                mRightContent!!.measuredWidth - abs(scrollX), 0, DURATION
            )
        } else {
            if (!mScroller.isFinished) mScroller.forceFinished(true)
            val scrollX = scrollX
            mScroller.startScroll(
                abs(scrollX), 0,
                -abs(scrollX), 0, DURATION
            )
        }
        invalidate()
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        initVelocityTrackerIfNotExists().addMovement(event)
        when (event!!.actionMasked) {
            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
                if (!mScroller.isFinished) {
                    mScroller.abortAnimation()
                }
                curX = 0f
                Log.e("SideSlipLayout", "curX: $curX")
                mActivePointerId = event.getPointerId(0)
            }
            MotionEvent.ACTION_MOVE -> {
                val scrollX = scrollX
                val measuredWidth: Int = mRightContent!!.measuredWidth
                Log.d("\"SideSlipLayout\"", "move: $scrollX")
                val moveX = event.x
                var distanceX = (curX - moveX).toInt()
                if (distanceX + scrollX < 0) {
                    distanceX = 0 - scrollX
                }
                if (distanceX + scrollX > measuredWidth) {
                    distanceX = measuredWidth - scrollX
                }
                if (curX != 0f) scrollBy(distanceX, 0)
                curX = moveX
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                curX = 0f
                val velocityTracker = mVelocityTracker
                velocityTracker?.also {
                    it.computeCurrentVelocity(1000, mMaximumVelocity.toFloat())
                    val initialVelocity = it.getXVelocity(mActivePointerId).toInt()
                    if (Math.abs(initialVelocity) > mMinimumVelocity) {
                        fling(-initialVelocity)
                    }
                    swipeOrOpenMenu(-initialVelocity)
                }
                recycleVelocityTracker()
            }
        }
        return true
    }

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        val action = ev.action
        if (action == MotionEvent.ACTION_MOVE && mIsBeingDragged) {
            return true
        }
        if (super.onInterceptTouchEvent(ev)) {
            return true
        }
        when (action and MotionEvent.ACTION_MASK) {
            MotionEvent.ACTION_MOVE -> {
                val activePointerId = mActivePointerId
                if (activePointerId != ViewDragHelper.INVALID_POINTER) {
                    val pointerIndex = ev.findPointerIndex(activePointerId)
                    if (pointerIndex != -1) {
                        Log.e(
                            "SideSlipLayout", "Invalid pointerId=" + activePointerId
                                    + " in onInterceptTouchEvent"
                        )
                        val x = ev.getX(pointerIndex).toInt()
                        val xDiff = Math.abs(x - mLastMotionX)
                        if (xDiff > mTouchSlop) {
                            mIsBeingDragged = true
                            mLastMotionX = x
                            initVelocityTrackerIfNotExists()
                            mVelocityTracker!!.addMovement(ev)
                            val parent = parent
                            parent?.requestDisallowInterceptTouchEvent(true)
                        }
                    }
                }
            }
            MotionEvent.ACTION_DOWN -> {
                mLastMotionX = ev.x.toInt()
                mActivePointerId = ev.getPointerId(0)
                initOrResetVelocityTracker()
                mVelocityTracker?.addMovement(ev)
                mScroller.computeScrollOffset()
                mIsBeingDragged = !mScroller.isFinished
            }
            MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
                mIsBeingDragged = false
                mActivePointerId = ViewDragHelper.INVALID_POINTER
                recycleVelocityTracker()
            }
            MotionEvent.ACTION_POINTER_UP -> onSecondaryPointerUp(ev)
        }
        return mIsBeingDragged
    }

    private fun onSecondaryPointerUp(ev: MotionEvent) {
        val pointerIndex = ev.action and MotionEvent.ACTION_POINTER_INDEX_MASK shr
                MotionEvent.ACTION_POINTER_INDEX_SHIFT
        val pointerId = ev.getPointerId(pointerIndex)
        if (pointerId == mActivePointerId) {
            val newPointerIndex = if (pointerIndex == 0) 1 else 0
            mLastMotionX = ev.getX(newPointerIndex).toInt()
            mActivePointerId = ev.getPointerId(newPointerIndex)
            if (mVelocityTracker != null) {
                mVelocityTracker!!.clear()
            }
        }
    }

    override fun computeScroll() {
        //先判断mScroller滚动是否完成
        if (mScroller.computeScrollOffset()) {
            val oldX = scrollX
            val oldY = scrollY
            val x = mScroller.currX
            val y = mScroller.currY
            if (oldX != x || oldY != y) {
                scrollBy(x - oldX, y - oldY)
            }
            ViewCompat.postInvalidateOnAnimation(this)
        }
    }

    private fun fling(xInitialVelocity: Int) {
        Log.d("SideSlipLayout", "fling: $xInitialVelocity")
        if (childCount > 0) {
            mScroller.fling(
                scrollX, scrollY, xInitialVelocity, 0, 0,
                mRightContent!!.measuredWidth,
                0, 0
            )
            ViewCompat.postInvalidateOnAnimation(this)
        }
    }

    private fun swipeOrOpenMenu(velocity: Int) {
        Log.d("SideSlipLayout", "swipeOrOpenMenu: ----->$velocity")
        val scrollX = scrollX
        val measuredWidth: Int = mRightContent!!.measuredWidth
        val halfScrollWidth = measuredWidth / 2
        if (Math.abs(velocity) <= mMinimumVelocity) {
            if (scrollX >= halfScrollWidth) {
                smoothOpenMenu(DURATION)
            } else {
                smoothCloseMenu(DURATION)
            }
        } else {
            if (velocity < 0) {
                smoothCloseMenu(DURATION)
            } else {
                smoothOpenMenu(DURATION)
            }
        }
    }

    private fun smoothOpenMenu(duration: Int) {
        sildListener?.onMenuState(true, this)
        if (!mScroller.isFinished) mScroller.forceFinished(true)
        val scrollX = scrollX
        mScroller.startScroll(
            Math.abs(scrollX), 0,
            mRightContent!!.measuredWidth - Math.abs(scrollX), 0, duration
        )
        invalidate()
    }

    private fun smoothCloseMenu(duration: Int) {
        sildListener?.onMenuState(false, this)
        if (!mScroller.isFinished) mScroller.forceFinished(true)
        val scrollX = scrollX
        mScroller.startScroll(
            Math.abs(scrollX), 0,
            -Math.abs(scrollX), 0, duration
        )
        invalidate()
    }


    private fun initVelocityTrackerIfNotExists(): VelocityTracker {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain()
        }
        return mVelocityTracker!!
    }

    private fun initOrResetVelocityTracker(): VelocityTracker {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain()
        } else {
            mVelocityTracker!!.clear()
        }
        return mVelocityTracker!!
    }

    private fun recycleVelocityTracker() {
        mVelocityTracker?.recycle()
        mVelocityTracker = null
    }
}